package com.nariano.server.domain.engine.droolsimpl;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.nariano.server.domain.entities.Area;
import com.nariano.server.domain.entities.Population;
import com.nariano.server.domain.entities.PopulationType;
import com.nariano.server.domain.entities.User;
import com.nariano.server.publisher.ClientPublisher;
import com.nariano.server.publisher.events.ConflictResolved;
import com.nariano.server.publisher.events.PopulationChanged;
import com.nariano.server.services.mapservice.MapService;
import com.nariano.server.services.userservice.UserService;

public class ResolveConflictAction {

	private static final Logger logger = Logger
			.getLogger(ResolveConflictAction.class.getCanonicalName());
	Random rand = new Random();

	@Autowired
	@Qualifier(value = "mapService")
	private MapService mapService;
	
	@Autowired
	@Qualifier(value = "userService")
	private UserService userService;
	
	@Autowired
	@Qualifier(value="clientPublisher")
    ClientPublisher clientPublisher;

	public ResolveConflictAction(MapService mapService, UserService userService) {
		this.mapService = mapService;
		this.userService = userService;
	}

	public ResolveConflictAction() {
	}

	public int resolve(Area a) {
		ConflictResolver c = new ConflictResolver(a);
		int causaltiesCount = c.resolve();
		mapService.updateAreas(a);
		return causaltiesCount;
	}

	private class ConflictResolver {

		private final Area area;
		private final List<Integer> users;

		public ConflictResolver(Area area) {
			users = new ArrayList<Integer>(area.getUsers());
			this.area = area;
		}

		public int resolve() {
			int count = 0;
			for (int i=0;i<area.getPopulationCount()-1;i++) {
				while (hasConflict() && twentyPercentChance()) {
					count += resolveConflict();
				}
			}
			if (!hasConflict()) {
				clientPublisher.publish(new ConflictResolved(area));
			}
			return count;
		}

		private boolean twentyPercentChance() {
			return rand.nextInt(5)==0;
		}

		private boolean hasConflict() {
			return area.getUsers().size() > 1;
		}

		private int resolveConflict() {
			// 1. Two random conflicting populations
			Integer user1 = getRandomUser(null);
			Integer user2 = getRandomUser(user1);

			Population population1 = area.moveOutRandomPopulation(user1);
			Population population2 = area.moveOutRandomPopulation(user2);
			
			logger.info("Duel at " + area.getLocation()
					+ " between " + population1 + " and " + population2);

			try {
				// 2. let them duel
				duel(population1, population2);			

				area.addPopulation(population1);
				area.addPopulation(population2);
				
			} catch (Exception e) {
				logger.severe("Error caught " + area.toString()+ e.getMessage());
			}
			
			if (logger.isLoggable(Level.FINE)) {
				logger.fine("Duel at " + area.getLocation()
						+ " between " + population1 + " and " + population2);
			}
			
			return 1; // now it's always always one 

		}

		private Integer getRandomUser(Integer excludingUser) {
			Integer res = null;
			do {
				res = users.get(rand.nextInt(users.size()));
			} while (excludingUser!=null && true == res.equals(excludingUser));
			return res;
		}

		private void duel(Population population1, Population population2) {
			PopulationType type1 = population1.type();
			PopulationType type2 = population2.type();
			int duelModifier = type1.getDuelModifier() - type2.getDuelModifier();
			if (rand.nextInt(10)+duelModifier < 5) {
				clientPublisher.publish(new PopulationChanged(area, population1.getUserId(), type1, -1));
				population1.setNumber(0);
				updateResources(population2.getUserId(), population1.getUserId(), population1.type().getLootPoints());
			} else {
				clientPublisher.publish(new PopulationChanged(area, population2.getUserId(), type2, -1));
				population2.setNumber(0);
				updateResources(population1.getUserId(), population2.getUserId(), population2.type().getLootPoints());
			}
		}

		private void updateResources(Integer winner, Integer looser, int lootPoints) {
			User u1 = userService.findUserFromId(winner);
			User u2 = userService.findUserFromId(looser);
			u1.setResources(u1.getResources()+lootPoints);
			u2.setResources(u2.getResources()-lootPoints);
			userService.updateUser(u1);
			userService.updateUser(u2);
			// todo notify loot!!!
		}
		

	}

}
